﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Media;
using TangoGames.Base;
using TangoGames.Scene;
using TangoGames.Primitives3D;


namespace incubation_xna3d.Scene
{
    class Stage:DrawableGameComponent, IStage
    {
        #region Fields

        /// <summary>
        /// Retangulo das dimensões do palco
        /// </summary>
        private Rectangle bounds;

        /// <summary>
        /// Camera do jogo
        /// </summary>
        private ICamera camera;

        /// <summary>
        /// Objetos lidos do XML
        /// </summary>
        private SceneObject[] objectsList = new SceneObject[0];

        /// <summary>
        /// Lista de modelos gerados no palco
        /// </summary>
        private Dictionary<int, GameObject3D> models = new Dictionary<int, GameObject3D>();

        /// <summary>
        /// Escala geral
        /// </summary>
        float mainScale = 6;

        private struct buindinPart
        {
            public Matrix world;
            public TexturedPrimitive texturePrimitive;
        }

        /// <summary>
        /// Primitivas para montar o prédio principal
        /// </summary>
        private buindinPart[] buidingParts;

        /// <summary>
        /// Safe Zone
        /// </summary>
        private TexturedPrimitive safeZone;

        #endregion

        #region Properties

        /// <summary>
        /// Lista de objetos definidos
        /// </summary>
        public SceneObject[] ObjectsList { get { return objectsList; } set { objectsList = value; } }

        /// <summary>
        /// Retangulo dos limites do palco
        /// </summary>
        public Rectangle Bounds { get { return bounds; } set { bounds = value; } }

        /// <summary>
        /// Lista de models carregados no Placo
        /// </summary>
        public Dictionary<int, GameObject3D> StageModels { get{ return models;} set { models= value;} }

        #endregion

        #region Construction

        /// <summary>
        /// função construtora
        /// </summary>
        /// <param name="game"></param>
        /// <param name="camera"></param>
        public Stage(Game game, ICamera camera)
            : base(game)
        {
            this.camera = camera;
            
            game.Components.Add(this);

            bounds = new Rectangle(-150, -150, 300, 300);

            BuildMainBuilding();

            InitSafeZone();

            BuildStage("Models/Scene/SceneObjects");

            BuildStageExternal("Models/Scene/ExternalObjects");
        }


        #endregion

        #region Methods

        /// <summary>
        /// Contruir Palco
        /// </summary>
        /// <param name="SceneObjectsXMLFileName"></param>
        public void BuildStage(String SceneObjectsXMLFileName)
        {
            // Carrega a lista de objecto.
            objectsList = Game.Content.Load<SceneObject[]>(SceneObjectsXMLFileName);

            for (int i = 0; i < objectsList.Length; i++)
            {
                models.Add(i, AddModel(i, objectsList[i]));
            }

        }

        /// <summary>
        /// Adiciona Modelo
        /// </summary>
        /// <param name="index"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public GameObject3D AddModel(int index, SceneObject obj)
        {
            GameObject3D model = new GameObject3D(Game, camera, Game.Content.Load<Model>(obj.ModelName));
            model.Position = new Vector3(obj.PosX, obj.PosY, obj.PosZ);
            model.RotationXDegrees = obj.RotationX;
            model.RotationYDegrees = obj.RotationY;
            model.Scale = mainScale * obj.Scale;
            model.Update();
            return model;
        }

        /// <summary>
        /// Carrega objectos externo ao palco
        /// </summary>
        /// <param name="ExternalObjectsXMLFileName"></param>
        public void BuildStageExternal(String ExternalObjectsXMLFileName)
        {
            // Carrega a lista de objecto.
            SceneObject[] objList = Game.Content.Load<SceneObject[]>(ExternalObjectsXMLFileName);
            GameObject3D model;

            for (int i = 0; i < objList.Length; i++)
            {
                model = AddModel(i, objList[i]);
                model.CallCustomizeEffect = new GameObject3D.CustomizeEffect(this.FogEffect);
            }

        }

        /// <summary>
        /// Aplicar efeito de fog
        /// </summary>
        /// <param name="effect"></param>
        public void FogEffect(BasicEffect effect)
        {
            effect.FogEnabled = true;
            effect.FogStart = 500;
            effect.FogEnd   = 800;
            effect.FogColor = new Vector3(0, 0, 0);
        }

        #endregion

        #region Draw
        public override void Draw(GameTime gameTime)
        {
            base.Draw(gameTime);
            DrawMainBuilding(camera.ViewMatrix, camera.ProjectionMatrix);
            DrawSafeZone(camera.ViewMatrix, camera.ProjectionMatrix);
        }
        #endregion

        #region Main Buiding Map

        /// <summary>
        /// Construir o Prédio Principal com Mapa
        /// </summary>
        private void BuildMainBuilding()
        {
            int buildingHeight = 1000;

            buidingParts = new buindinPart[5];

            //terraço
            buidingParts[0].texturePrimitive = new TexturedPrimitive(Game.GraphicsDevice, Game.Content.Load<Texture2D>("Textures/Scene/ground"));
            buidingParts[0].world = Matrix.CreateScale(new Vector3(bounds.Width, 0, bounds.Height));
            buidingParts[0].texturePrimitive.BasicEffect.AmbientLightColor = new Vector3(255, 255, 255);

            //lado da frente
            buidingParts[1].texturePrimitive = new TexturedPrimitive(Game.GraphicsDevice, Game.Content.Load<Texture2D>("Textures/Scene/building"));
            buidingParts[1].world = Matrix.CreateScale(new Vector3(bounds.Width, 0, buildingHeight))
                             * Matrix.CreateRotationX(MathHelper.ToRadians(90))
                             * Matrix.CreateTranslation(0, -buildingHeight / 2, bounds.Height / 2);


            //lado esquerdo
            buidingParts[2].texturePrimitive = new TexturedPrimitive(Game.GraphicsDevice, Game.Content.Load<Texture2D>("Textures/Scene/building"));
            buidingParts[2].world = Matrix.CreateScale(new Vector3(bounds.Height, 0, buildingHeight))
                            * Matrix.CreateRotationX(MathHelper.ToRadians(90))
                            * Matrix.CreateRotationY(MathHelper.ToRadians(-90))
                            * Matrix.CreateTranslation(-bounds.Width / 2, -buildingHeight / 2, 0);

            //lado direito
            buidingParts[3].texturePrimitive = new TexturedPrimitive(Game.GraphicsDevice, Game.Content.Load<Texture2D>("Textures/Scene/building"));
            buidingParts[3].world = Matrix.CreateScale(new Vector3(bounds.Height, 0, buildingHeight))
                             * Matrix.CreateRotationX(MathHelper.ToRadians(90))
                             * Matrix.CreateRotationY(MathHelper.ToRadians(90))
                             * Matrix.CreateTranslation(bounds.Width / 2, -buildingHeight / 2, 0);

            //lado de traz
            buidingParts[4].texturePrimitive = new TexturedPrimitive(Game.GraphicsDevice, Game.Content.Load<Texture2D>("Textures/Scene/building"));
            buidingParts[4].world = Matrix.CreateScale(new Vector3(bounds.Width, 0, buildingHeight))
                            * Matrix.CreateRotationX(MathHelper.ToRadians(90))
                            * Matrix.CreateRotationY(MathHelper.ToRadians(180))
                            * Matrix.CreateTranslation(0, -buildingHeight / 2, -bounds.Height / 2);


            for (int i = 1; i < 5; i++)
            {
                FogEffect(buidingParts[i].texturePrimitive.BasicEffect);
                buidingParts[i].texturePrimitive.BasicEffect.AmbientLightColor = new Vector3(255, 255, 255);
            }


        }

        /// <summary>
        /// Cria a safe zone
        /// </summary>
        private void InitSafeZone()
        {
            safeZone = new TexturedPrimitive(Game.GraphicsDevice, Game.Content.Load<Texture2D>("Textures/Scene/extracao"));
        }

        private void DrawSafeZone(Matrix view, Matrix proj)
        {
            safeZone.Draw(Matrix.CreateScale(40.0F) * Matrix.CreateTranslation( 120.0F, 0.1F , 120.0F ), view, proj);
        }

        /// <summary>
        /// Desenha o prédio principal
        /// </summary>
        /// <param name="graphicsDevice"></param>
        /// <param name="view"></param>
        /// <param name="proj"></param>
        private void DrawMainBuilding(Matrix view, Matrix proj)
        {
            foreach (buindinPart bp in buidingParts)
            {
                bp.texturePrimitive.Draw(bp.world, view, proj);
            }
        }

        #endregion
    }
}
